#include "CityMap.h"
#include "GFuns.h"

CityMap::CityMap(Character* aHero, int aSeed, int aBiome) : Map(aHero, aSeed){
	fprintf(stderr, "> City map constructor...");

	_mapType = MAP_CITY;
	_biome = aBiome;

	Generate(60,60);

	fprintf(stderr, " - OK\n");
}

int CityMap::placeCityGrid(vector<sRectangle> aBorders, int aObject, bool aDoors){
	int maxArea = 0;
	int maxAreaIndex = 0;
	for(unsigned int i=0; i<aBorders.size(); i++){
		for(int x=aBorders[i].x1; x<=aBorders[i].x2; x++){
			setUpper(x,aBorders[i].y1,aObject);
			setUpper(x,aBorders[i].y2,aObject);
		}
		for(int y=aBorders[i].y1; y<=aBorders[i].y2; y++){
			setUpper(aBorders[i].x1,y,aObject);
			setUpper(aBorders[i].x2,y,aObject);
		}
		if(aDoors){
			int doorsX1 = randInt(aBorders[i].x1+1, aBorders[i].x2-1);
			int doorsX2 = randInt(aBorders[i].x1+1, aBorders[i].x2-1);
			int doorsY1 = randInt(aBorders[i].y1+1, aBorders[i].y2-1);
			int doorsY2 = randInt(aBorders[i].y1+1, aBorders[i].y2-1);
			setUpper(doorsX1,aBorders[i].y1,OBJECT_NONE);
			setUpper(doorsX2,aBorders[i].y2,OBJECT_NONE);
			setUpper(aBorders[i].x1,doorsY1,OBJECT_NONE);
			setUpper(aBorders[i].x2,doorsY2,OBJECT_NONE);
		}

		if(aBorders[i].area() > maxArea){
			maxArea = aBorders[i].area();
			maxAreaIndex = i;
		}
	}
	return maxAreaIndex;
}

int CityMap::placeArea(vector<sRectangle> aBorders, int aObject, bool aDoors){
	int maxArea = 0;
	int maxAreaIndex = 0;
	for(unsigned int i=0; i<aBorders.size(); i++){
		for(int x=aBorders[i].x1; x<=aBorders[i].x2; x++){
			_BackTiles(x,aBorders[i].y1).upper = aObject;
			_BackTiles(x,aBorders[i].y2).upper = aObject;
		}
		for(int y=aBorders[i].y1; y<=aBorders[i].y2; y++){
			_BackTiles(aBorders[i].x1,y).upper = aObject;
			_BackTiles(aBorders[i].x2,y).upper = aObject;
		}
		if(aDoors){
			int doorsX1 = randInt(aBorders[i].x1+1, aBorders[i].x2-1);
			int doorsX2 = randInt(aBorders[i].x1+1, aBorders[i].x2-1);
			int doorsY1 = randInt(aBorders[i].y1+1, aBorders[i].y2-1);
			int doorsY2 = randInt(aBorders[i].y1+1, aBorders[i].y2-1);
			_BackTiles(doorsX1,aBorders[i].y1).upper = OBJECT_NONE;
			_BackTiles(doorsX2,aBorders[i].y2).upper = OBJECT_NONE;
			_BackTiles(aBorders[i].x1,doorsY1).upper = OBJECT_NONE;
			_BackTiles(aBorders[i].x2,doorsY2).upper = OBJECT_NONE;
		}

		if(aBorders[i].area() > maxArea){
			maxArea = aBorders[i].area();
			maxAreaIndex = i;
		}
	}
	return maxAreaIndex;
}

void CityMap::placeRectangle(sRectangle aRect, int aObject, int aFloor){
	for(int x=aRect.x1; x<=aRect.x2; x++){
		_BackTiles(x,aRect.y1).upper = aObject;
		_BackTiles(x,aRect.y2).upper = aObject;
	}
	for(int y=aRect.y1; y<=aRect.y2; y++){
		_BackTiles(aRect.x1,y).upper = aObject;
		_BackTiles(aRect.x2,y).upper = aObject;
	}

	if(aFloor != FLOOR_NONE) placeFloor(aRect, aFloor);
}

void CityMap::placeFloor(sRectangle aRect, int aFloor){
	for(int x=aRect.x1; x<=aRect.x2; x++)
		for(int y=aRect.y1; y<=aRect.y2; y++)
			_BackTiles(x,y).lower = aFloor;
}

void CityMap::flipBackTiles(bool aV){
	if(aV){
		for(int x=0; x<_BackTiles.getSizeW()/2; x++){
			for(int y=0; y<_BackTiles.getSizeH(); y++){
				Tile t = _BackTiles(x,y);
				_BackTiles(x,y) = _BackTiles(_BackTiles.getSizeW()-1-x,y);
				_BackTiles(_BackTiles.getSizeW()-1-x,y) = t;
			}
		}
	}
	else{
		for(int y=0; y<_BackTiles.getSizeH()/2; y++){
			for(int x=0; x<_BackTiles.getSizeW(); x++){
				Tile t = _BackTiles(x,y);
				_BackTiles(x,y) = _BackTiles(x,_BackTiles.getSizeH()-1-y);
				_BackTiles(x,_BackTiles.getSizeH()-1-y) = t;
			}
		}
	}
}

void CityMap::pasteBackTiles(sRectangle aRect, bool aRot){
	if(!aRot){
		for(int x=aRect.x1+1; x<aRect.x2; x++)
			for(int y=aRect.y1+1; y<aRect.y2; y++){
				setLower(x,y,_BackTiles(x-aRect.x1,y-aRect.y1).lower);
				setUpper(x,y,_BackTiles(x-aRect.x1,y-aRect.y1).upper);
				setSpawn(x,y,_BackTiles(x-aRect.x1,y-aRect.y1).spawn);
			}
	}
	else{
		for(int x=aRect.x1+1; x<aRect.x2; x++)
			for(int y=aRect.y1+1; y<aRect.y2; y++){
				setLower(x,y,_BackTiles(y-aRect.y1,x-aRect.x1).lower);
				setUpper(x,y,_BackTiles(y-aRect.y1,x-aRect.x1).upper);
				setSpawn(x,y,_BackTiles(y-aRect.y1,x-aRect.x1).spawn);
			}
	}
}

int CityMap::objectsNextTo(Node aActual, int aObject){
	int objects = 0;
	for(int d=0; d<4; d++){
		if(_BackTiles(aActual.x + dir[d][0], aActual.y + dir[d][1]).upper == aObject)
			objects++;
	}
	return objects;
}

void CityMap::GenerateMapData(){
	fprintf(stderr, "\n> City generator\n");

	// Soil background
	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		setLower(x,y,F_SOIL);
	}

	// setup city grid
	fprintf(stderr, "  - City grid\n");
	BSP cityGrid;
	vector<sRectangle> roads = cityGrid.produce(-1,-1,_tilesX,_tilesY,4);
	placeCityGrid(roads,O_ROAD);

	// preparing building list
	fprintf(stderr, "  - Building list\n");
	struct sBuilding{
		EBuilding type;
		int minW;
		int minH;
	};
	vector<sBuilding> buildings;
	sBuilding market = {B_MARKET, 5, 5};
	sBuilding palace = {B_PALACE, 27, 17};
	sBuilding inn = {B_INN, 13, 15};
	sBuilding temple = {B_TEMPLE, 13, 13};
	sBuilding weapon = {B_WEAPONSHOP, 9, 9};
	sBuilding armor = {B_ARMORSHOP, 9, 9};
	sBuilding herb = {B_HERBSHOP, 9, 9};
	buildings.push_back(market);
	buildings.push_back(palace);
	buildings.push_back(inn);
	buildings.push_back(temple);
	buildings.push_back(weapon);
	buildings.push_back(armor);
	buildings.push_back(herb);
	int emptyAreas = roads.size()-buildings.size();
	sBuilding house = {B_HOUSE, 9, 9};
	for(int i=0; i<emptyAreas; i++) buildings.push_back(house);
	sBuilding graveyard = {B_GRAVEYARD, 5, 5};
	buildings.push_back(graveyard);

	// boxing building
	fprintf(stderr, "  - Place buildings\n");
	for(unsigned int b=0; b<buildings.size() && !roads.empty(); b++){
		int index = 0;
		unsigned int counter = 0;
		do{
			index = rand()%(roads.size());
			if((roads[index].x2-roads[index].x1 > buildings[b].minW && roads[index].y2-roads[index].y1 > buildings[b].minH)
				|| (roads[index].x2-roads[index].x1 > buildings[b].minH && roads[index].y2-roads[index].y1 > buildings[b].minW)){
					switch(buildings[b].type){
					case B_PALACE: generatePalace(roads[index]); break;
					case B_INN: generateInn(roads[index]); break;
					case B_TEMPLE: generateTemple(roads[index]); break;
					case B_WEAPONSHOP: generateShop(roads[index], B_WEAPONSHOP); break;
					case B_ARMORSHOP: generateShop(roads[index], B_ARMORSHOP); break;
					case B_HERBSHOP: generateShop(roads[index], B_HERBSHOP); break;
					case B_HOUSE: generateHouse(roads[index]); break;
					case B_GRAVEYARD: generateGraveyard(roads[index]); break;
					case B_MARKET: generateMarket(roads[index]); break;
					}
					roads.erase(roads.begin()+index);
					break;
			}
			if(++counter > roads.size()*4) break;
		}while(true);
	}

	// A bunch of trees
	for(unsigned int r=0; r<roads.size(); r++)
		for(int x=roads[r].x1+1; x<roads[r].x2; x++)
		for(int y=roads[r].y1+1; y<roads[r].y2; y++)
		{
			if(rand()%3 == 0) setUpper(x,y,O_TREE);
		}

	// Poor unemployed people plus well list
	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		if(getUpper(x,y) == OBJECT_NONE){
			int c = 0;
			for(int d=0; d<4; d++)
				if(getUpper(x+dir[d][0],y+dir[d][1]) == O_WALL)
					c++;
			if(c==3) setUpper(x,y,O_WALL);
		}
		if(getUpper(x,y) == O_ROAD || (getLower(x,y) == F_SOIL && getUpper(x,y) == OBJECT_NONE)){
			if(rand()%40 == 0) setSpawn(x,y,JOB_UNEMPLOYED);
		}
		if(getUpper(x,y) == O_WELL)
		{
			Node n = {x,y};
			wellList.push_back(n);
		}
	}

	// Passable tile set
	for(int x=0; x<_tilesX; x++)
	for(int y=0; y<_tilesY; y++)
	{
		int t = getUpper(x,y);
		if(t == OBJECT_NONE || t == O_ROAD || t == O_CHAIR || t == O_BENCH || t == O_WELL) SetPassable(x,y,true);
	}

	// Weather
	int weatherProb[] = {0,0,0,0,0,0,0, 5,2,8,10,5,8,8,5};
	if(_biome == POLAR) weather = new Weather(WEATHER_SNOW);
	else {
		for(int b = SAVANNA; b < POLAR; b++)
			if(b == _biome && (rand()%10)+1 > weatherProb[b])
				weather = new Weather(WEATHER_RAIN);
	}
}

Node CityMap::GetHeroSpawn(int facing)
{
	if(facing == RIGHT || facing == LEFT)
	{
		int x = (facing == RIGHT ? 0 : _tilesX-1);

		int mid = _tilesY/2;
		int up = 0;
		int down = _tilesY-1;

		for(int y=mid; y<_tilesY; y++)
			if(getUpper(x,y) == O_ROAD)
			{
				down = y;
				break;
			}
		for(int y=mid; y>-1; y--)
			if(getUpper(x,y) == O_ROAD)
			{
				up = y;
				break;
			}

		Node n = {x, (down-mid < mid-up ? down : up)};
		return n;
	}
	else if(facing == UP || facing == DOWN)
	{
		int y = (facing == DOWN ? 0 : _tilesY-1);

		int mid = _tilesX/2;
		int left = 0;
		int right = _tilesX-1;

		for(int x=mid; x<_tilesX; x++)
			if(getUpper(x,y) == O_ROAD)
			{
				right = x;
				break;
			}
		for(int x=mid; x>-1; x--)
			if(getUpper(x,y) == O_ROAD)
			{
				left = x;
				break;
			}

		Node n = {(right-mid < mid-left ? right : left), y};
		return n;
	}
	else
	{
		return wellList[rand()%wellList.size()];
	}

	Node n = {0,0};
	return n;
}